package com.google.code.mergeinformer.model.security;


import com.google.code.mergeinformer.model.DefaultEntity;
import com.google.code.mergeinformer.model.Language;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import javax.persistence.*;
import javax.validation.constraints.Pattern;
import javax.validation.constraints.Size;
import java.util.*;

/**
 * @author Dmitriy Golubev
 */
@NamedQueries({
        @NamedQuery(name = "User.byUsername", query = "SELECT u FROM User u WHERE u.username = :username")
})
@Entity
@Table(name = "app_user")
public class User extends DefaultEntity implements UserDetails {

    @NotBlank
    @Size(max = 32)
    @Column(name = "username", nullable = false, unique = true)
    private String username;

    @Pattern(regexp = "[0-9a-f]{64}", flags = Pattern.Flag.CASE_INSENSITIVE) // SHA-256 hash format
    @Column(name = "password", nullable = false)
    private String password;

    @Enumerated(EnumType.STRING)
    @Column(name = "language", nullable = false)
    private Language language = Language.EN;

    @Column(name = "enabled", nullable = false)
    private boolean enabled;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(
            name = "app_user2authority_group",
            joinColumns = @JoinColumn(name = "id_app_user"),
            inverseJoinColumns = @JoinColumn(name = "id_authority_group")
    )
    private Set<AuthorityGroup> authorityGroups = new HashSet<>();

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "user")
    private List<RememberMeToken> rememberMeTokens = new ArrayList<>();

    @Override
    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Set<AuthorityGroup> getAuthorityGroups() {
        return authorityGroups;
    }

    public Language getLanguage() {
        return language;
    }

    public void setLanguage(Language language) {
        this.language = language;
    }

    public void setAuthorityGroups(Set<AuthorityGroup> authorityGroups) {
        this.authorityGroups = authorityGroups;
        if (authorityGroups != null) {
            for (AuthorityGroup authorityGroup : authorityGroups) {
                authorityGroup.addUser(this);
            }
        }
    }

    public void addAuthorityGroup(AuthorityGroup authorityGroup) {
        if (authorityGroup != null && !authorityGroups.contains(authorityGroup)) {
            authorityGroups.add(authorityGroup);
            authorityGroup.addUser(this);
        }
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        final Set<Authority> authorities = new HashSet<>();
        final Set<AuthorityGroup> authGroups = getAuthorityGroups();
        if (authGroups != null) {
            for (AuthorityGroup authorityGroup : authGroups) {
                authorities.addAll(authorityGroup.getAuthorities());
            }
        }
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    public void addRememberMeToken(RememberMeToken token) {
        if (rememberMeTokens.contains(token)) {
            return;
        }
        rememberMeTokens.add(token);
        token.setUser(this);
    }

    public boolean removeRememberMeToken(RememberMeToken token) {
        return rememberMeTokens.remove(token);
    }
}
